import datetime
import json
import re
import time

from selenium.common import NoSuchElementException
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By

from libs import config
from libs.account import Account
from libs.action import WebDriverAction
from libs.db import DB
from libs.game import Game
from libs.image import ImageTool
from libs.log import Log
from libs.movement import Movement


class FailReason:
    ITEMS_NOT_LOADED = 'items_not_loaded'
    NO_AVAILABLE_ITEMS = 'no_available_items'
    NO_LISTINGS = 'no_listings'
    CANNOT_AFFORD = 'cannot_afford'
    LOW_TRUST_SCORE = 'low_trust_score'
    PRICE_EXCEED_LIMIT = 'price_exceed_limit'
    EXCEEDS_PURCHASE_LIMIT = 'exceeds_purchase_limit'
    NO_SPACE = 'no_space'


class BuyResult:
    def __init__(self):
        self.success = True
        self.price = None
        self.fail_reason = None
        self.qty = None

    def buy_success(self, qty, price):
        self.success = True
        self.qty = qty
        self.price = price
        return self

    def buy_fail(self, fail_reason, qty=0):
        self.success = False
        self.fail_reason = fail_reason
        self.qty = qty
        return self


class TaskItem:
    def __init__(self, name, reward, qty, is_p, can_deliver, is_new):
        self.name = name
        self.reward = reward
        self.qty = qty
        self.is_p = is_p
        self.can_deliver = can_deliver
        self.price = None
        self.is_new = is_new
        self.purchased = False
        self.trade_config = {}

    def get_cost(self):
        if self.price is not None:
            return self.price * self.qty - self.reward
        return None

    def can_afford(self, my_coin, is_deliver=False):
        cost = self.get_cost()
        ratio = 2 if is_deliver else 1
        if cost is None:
            return False
        else:
            return cost <= self.get_cost_limit(my_coin) * ratio

    def is_on_sale(self):
        return self.name not in config.get_no_price_item_names()

    def get_cost_limit(self, my_coin):
        max_cost = int(self.trade_config.get('max_cost', 0))
        max_p_cost = int(self.trade_config.get('max_p_cost', 0))
        cost_ratio = float(self.trade_config.get('cost_ratio', 0))
        limit = min(max_cost, int(my_coin * cost_ratio))
        if self.is_p:
            limit = max_p_cost * self.reward
        return limit

    def to_json(self, indent=None):
        obj = {
            'item': f"{'[new]' if self.is_new else ''}{'[ready]' if self.can_deliver else ''}{self.name} x {self.qty}",
            'reward': f"{self.reward}{'[P]' if self.is_p else ''}",
        }
        if self.price is not None:
            if self.is_p:
                obj['cost'] = f"{self.price} x {self.qty} = {self.price * self.qty}"
            else:
                obj['cost'] = f"{self.price} x {self.qty} - {self.reward} = {self.price * self.qty - self.reward}"
        return json.dumps(obj, indent=indent)


class TradeHelper:
    def __init__(self, driver, user_id):
        self.driver = driver
        self.user_id = user_id
        self.log = Log(user_id)
        self.can_trade = config.can_trade(user_id)
        self.is_vip = config.is_vip(user_id)
        self.action = WebDriverAction(driver, False)
        self.movement = Movement(driver)
        self.image_tool = ImageTool(driver)
        self.ac = ActionChains(driver)
        self.game = Game(driver, user_id)
        self.shop_buy = 'shop_buy.png'
        self.shop_sell = 'shop_sell.png'
        self.tasks = {}
        self.market_cooldown_seconds = 12
        self.last_trade_time = None
        self.db = DB()
        self.trade_config = {}
        self.init_trade_config()

        item_price_init_sql = """
            CREATE TABLE IF NOT EXISTS item_price (
                id INTEGER PRIMARY KEY,
                item_name TEXT NOT NULL,
                price INTEGER NOT NULL,
                last_update DATETIME NULL
            )
        """
        self.db.run_sql(item_price_init_sql)

        taskboard_init_sql = """
            CREATE TABLE IF NOT EXISTS task_board (
                id INTEGER PRIMARY KEY,
                user_id INTEGER NOT NULL,
                game_date DATE NOT NULL,
                create_time DATETIME NOT NULL,
                position INTEGER NOT NULL,
                item_name TEXT NOT NULL,
                qty INTEGER NOT NULL,
                reward DECIMAL DEFAULT 0,
                is_p BOOLEAN DEFAULT 0,
                item_price INTEGER NULL,
                cost INTEGER NULL
            );
        """
        self.db.run_sql(taskboard_init_sql)

        deliver_init_sql = """
            CREATE TABLE IF NOT EXISTS deliver (
                id INTEGER PRIMARY KEY,
                user_id INTEGER NOT NULL,
                game_date DATE NOT NULL,
                create_time DATETIME NOT NULL,
                position INTEGER NOT NULL,
                item_name TEXT NOT NULL,
                qty INTEGER NOT NULL,
                reward DECIMAL DEFAULT 0,
                is_p BOOLEAN DEFAULT 0,
                item_price INTEGER NULL,
                cost INTEGER NULL,
                type TEXT
            );
        """
        self.db.run_sql(deliver_init_sql)

    def init_trade_config(self):
        user_trade_config = config.get_env(f"trade_{self.user_id}")
        if user_trade_config is not None:
            self.trade_config = user_trade_config
        else:
            if self.is_vip:
                self.trade_config = config.get_vip_trade_config()
            else:
                self.trade_config = config.get_trade_config()

    def get_game_date(self):
        now = datetime.datetime.now()
        hour = now.hour
        date = now.strftime("%Y-%m-%d")
        if hour < 8:
            date = (now - datetime.timedelta(days=1)).strftime("%Y-%m-%d")
        return date

    def snapshot(self):
        self.db.run_sql("DELETE FROM task_board where game_date = ? and user_id = ?", (self.get_game_date(), self.user_id))
        insert_sql = """
        INSERT INTO task_board (user_id, game_date, create_time, position, item_name, qty, reward, is_p, item_price, cost)
        VALUES (?,?,?,?,?,?,?,?,?,?)
        """
        batch_data = []
        for index in self.tasks:
            task:TaskItem = self.tasks.get(index)
            batch_data.append((
                self.user_id,
                self.get_game_date(),
                datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                index,
                task.name,
                task.qty,
                task.reward,
                task.is_p,
                task.price,
                task.get_cost()
            ))
        self.db.run_batch(insert_sql, batch_data)

    def save_deliver(self, index, task: TaskItem):
        insert_sql = """
            INSERT INTO deliver 
            (user_id, game_date, create_time, position, item_name, qty, reward, is_p, item_price, cost, type)
            VALUES (?,?,?,?,?,?,?,?,?,?,?)
            """
        self.db.run_sql(insert_sql, (
            self.user_id,
            self.get_game_date(),
            datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            index,
            task.name,
            task.qty,
            task.reward,
            task.is_p,
            task.price,
            task.get_cost(),
            'market' if task.purchased else 'backpack'
        ))
    @staticmethod
    def clear():
        db = DB()
        row = db.fetch_one("SELECT count(*) as row_count from task_board where  create_time <= DATETIME('now', '-3 days')")
        print(row)
        db.run_sql("DELETE FROM task_board where create_time <= DATETIME('now', '-3 days')")
        print('task_board data cleared')

    def save_item_price(self, name, price):
        self.db.run_sql("DELETE from item_price where item_name = ? ", (name,))
        insert_sql = """
        INSERT INTO item_price (item_name, price, last_update)
        VALUES(?, ?, ?)
        """
        datetime_format = '%Y-%m-%d %H:%M:%S'
        self.db.run_sql(insert_sql, (name, price, datetime.datetime.now().strftime(datetime_format)))

    def get_item_prices(self):
        sql = """
        SELECT item_name, price from item_price where last_update >=  DATETIME('now', '-24 hours')
        """
        rows = self.db.fetch_all(sql)
        result = {}
        for row in rows:
            result[row[0]] = row[1]
        return result

    def move_to_shop_top(self):
        self.movement.keep_move_right(0.2)
        galore_image = 'the_bucks_galore.png'
        galore_loc = self.image_tool.find_target(galore_image)
        if galore_loc is not None:
            self.action.move_to(galore_image, delta_x=-150, max_loop=3)
        else:
            self.movement.keep_move_right(0.6)
            self.movement.keep_move_up(3)
        self.movement.keep_move_up(4)

    def move_to_buy_from_door(self):
        self.move_to_shop_top()
        time.sleep(1)
        for _ in range(3):
            shop_buy_loc = self.image_tool.find_target(self.shop_buy)
            if shop_buy_loc is not None:
                self.action.move_to(self.shop_buy, max_loop=2)
                break

    def move_to_sell_from_door(self):
        self.move_to_shop_top()
        time.sleep(1)
        for _ in range(3):
            shop_sell_loc = self.image_tool.find_target(self.shop_sell)
            if shop_sell_loc is not None:
                self.action.move_to(self.shop_sell, max_loop=2)
                break

    def goto_seed_store(self):
        buck_loc = self.image_tool.find_target('the_bucks_galore.png')
        if buck_loc is None:
            self.action.goto_shop()
        self.action.move_to('the_bucks_galore.png', delta_x=20, delta_y=120, max_loop=4)

    def goto_buy_position(self):
        self.game.enter_game(self.user_id)
        buy_loc = self.image_tool.find_target(self.shop_buy)
        if buy_loc is not None:
            self.log.debug('see shop buy, in shop buy position')
            self.action.move_to(self.shop_buy, max_loop=3)
        else:
            self.action.goto_shop()
            self.move_to_buy_from_door()

    def goto_sell_position(self):
        self.game.enter_game(self.user_id)
        sell_loc = self.image_tool.find_target(self.shop_sell)
        if sell_loc is not None:
            self.log.debug('see shop sell, in shop sell position')
        else:
            self.action.goto_shop()
            self.move_to_sell_from_door()

    def buy_to_sell(self):
        self.movement.keep_move_left(2)
        time.sleep(1)

    def sell_to_buy(self):
        self.movement.keep_move_right(3)
        time.sleep(1)
        self.movement.keep_move_left(0.7)
        time.sleep(1)

    def get_number(self, txt):
        is_k_value = False
        txt = txt.replace(' ', '')
        if 'K' in txt:
            is_k_value = True
            txt = txt.replace('K', '')
        else:
            txt = txt.replace('.', '')
        value = float(txt.replace(',', ''))
        if is_k_value:
            value = value * 1000
        return int(value)

    def open_orders(self):
        orders_container = self.action.find_element(By.CLASS_NAME,
                                                    'Store_items-content__FtMRE',
                                                    'OrderContainer',
                                                    1)
        if orders_container is None:
            for _ in range(5):
                tb_loc = self.image_tool.find_target('task_board.png')
                if tb_loc is not None:
                    tb_x, tb_y = tb_loc
                    self.action.click_by_offset(tb_x + 20, tb_y)
                    time.sleep(3)
                    order_button = self.action.find_element(By.XPATH, "//button[text()='Orders']", 'Orders', 10)
                    if order_button is not None:
                        order_button.click()
                        time.sleep(1)
                        orders_container = self.action.find_element(By.CLASS_NAME, 'Store_items-content__FtMRE',
                                                                    'OrderContainer',
                                                                    15)
                        if orders_container is not None:
                            break
                self.action.escape()
                self.movement.keep_move_up(1)
        time.sleep(1)
        return orders_container

    def open_order_items(self, filter_input, item_name):
        if filter_input.get_attribute('value') != item_name:
            filter_input.send_keys(Keys.CONTROL, 'a')
            filter_input.send_keys(Keys.BACKSPACE)
            filter_input.send_keys(item_name)
        time.sleep(0.5)

        if item_name == 'Egg':
            self.action.find_and_click_by_xpath(
                f"(//div[text()='{item_name}']/following-sibling::button)[last()]",
                item_name,
                15
            )
        else:
            self.action.find_and_click_by_xpath(
                f"//div[text()='{item_name}']/following-sibling::button",
                item_name,
                15
            )
        time.sleep(0.5)
        auto_buy = None
        listed_items = []
        for _ in range(10):
            order_items = self.action.find_elements(
                By.CLASS_NAME,
                'MarketplaceItemListings_buyListing__jYwuF',
                'OrderItems',
                5
            )
            if order_items is None:
                time.sleep(1)
                continue
            if len(order_items) == 1:
                time.sleep(1)
                continue
            for order_item in order_items:
                if order_item.text == 'Automatic Buy':
                    auto_buy = order_item
                else:
                    listed_items.append(order_item)
            break
        time.sleep(0.5)
        return auto_buy, listed_items

    def open_market_buy(self):
        filter_input = None
        for _ in range(5):
            shop_buy_loc = self.image_tool.find_target(self.shop_buy)
            if shop_buy_loc is not None:
                shop_buy_x, shop_buy_y = shop_buy_loc
                self.action.click_by_offset(shop_buy_x + 18, shop_buy_y + 80)
                time.sleep(1)
                filter_input = self.action.find_element(By.CLASS_NAME, 'Marketplace_filter__3ynr2', 'FilterInput', 5)
                if filter_input is not None:
                    break
            self.action.escape()
            self.action.move_to(self.shop_buy, max_loop=2)
        time.sleep(1)
        return filter_input

    def open_shop_sell(self):
        for _ in range(5):
            shop_sell_loc = self.image_tool.find_target(self.shop_sell)
            if shop_sell_loc is not None:
                shop_sell_x, shop_sell_y = shop_sell_loc
                self.action.click_by_offset(shop_sell_x + 20, shop_sell_y)
                time.sleep(1)
                create_button = self.action.find_element(By.XPATH, '//button[text()="Create"]', "Create", 5)
                if create_button is not None:
                    break
                self.action.move_to(self.shop_sell, max_loop=2)
            time.sleep(1)

    def open_seed_buy(self):
        self.action.escape()
        buck_loc = self.image_tool.find_target('shop_buck.png')
        if buck_loc is not None:
            buck_x, buck_y = buck_loc
            self.action.click_by_offset(buck_x - 20, buck_y)
        time.sleep(1)

    def buy_seed(self, seed_name, buy_qty):
        time.sleep(1)
        for _ in range(3):
            self.action.find_and_click_by_xpath(f"//div[text()='{seed_name}']/parent::div/parent::div", seed_name, 2)
            time.sleep(2)
            qty_input = self.action.find_element(By.CLASS_NAME, 'Store_quantity-input__dNych', 'QuantityInput', 2)
            if qty_input is not None:
                qty_input.send_keys(Keys.BACKSPACE)
                time.sleep(1)
                qty_input.send_keys(str(buy_qty))
                time.sleep(1)
                self.action.find_and_click_by_class('Store_buy-btn__gLsMk', 'BuyButton')
                self.log.debug('buy_seed:' + json.dumps({'name': seed_name, 'qty': buy_qty}))
                break

    def market_buy_item(self, filter_input, item_name, buy_qty):
        if buy_qty <= 0:
            return
        self.action.click_by_offset(675, 165)
        self.log.debug(f'buy_item: {item_name} = {buy_qty}')
        auto_buy, listed_items = self.open_order_items(filter_input, item_name)
        if len(listed_items) == 0:
            return BuyResult().buy_fail(FailReason.ITEMS_NOT_LOADED)

        floor_item = listed_items[len(listed_items) - 1]
        floor_price = self.get_number(floor_item.text.split(' @ ')[1])
        self.save_item_price(item_name, floor_price)

        time.sleep(0.5)
        left_qty = buy_qty
        auto_buy.click()
        time.sleep(0.5)
        for _ in range(20):
            time.sleep(1)
            batch_buy_qty = min(99, left_qty)
            max_amount_input = self.action.find_element(By.XPATH,
                                                        f"//span[text()='Maximum Amount']/following-sibling::input",
                                                        'Maximum Amount', 5)
            if int(max_amount_input.get_attribute('value')) != batch_buy_qty:
                time.sleep(0.5)
                max_amount_input.send_keys(Keys.CONTROL, 'a')
                time.sleep(0.5)
                max_amount_input.send_keys(str(batch_buy_qty))
            time.sleep(1)
            buy_button = self.action.find_element(By.XPATH, f"//button[starts-with(text(), 'Buy {batch_buy_qty} for')]",
                                                  'Buy Button', 5)
            if self.last_trade_time is not None:
                duration = (datetime.datetime.now() - self.last_trade_time).seconds
                if duration < self.market_cooldown_seconds:
                    time.sleep(self.market_cooldown_seconds - duration)
            buy_button.click()
            self.last_trade_time = datetime.datetime.now()
            self.log.debug_start('check buy result..')
            purchased = False
            cannot_afford = False
            no_listing = False
            no_space = False
            purchase_limit = False
            for _ in range(10):
                try:
                    notify_elem = self.driver.find_element(By.CLASS_NAME, 'Notifications_text__ak1FH')
                    notify_text = notify_elem.text
                    time.sleep(2)
                    if notify_text == 'cannot-afford':
                        cannot_afford = True
                        break
                    if notify_text == 'no-listings-found':
                        no_listing = True
                        break
                    if notify_text == 'exceeds-purchase-limit':
                        purchase_limit = True
                        break
                    if notify_text == 'Not enough space in Inventory!':
                        no_space = True
                        break

                    self.log.debug_append('.')
                    self.driver.find_element(By.XPATH, "//h1[text()='Purchased']")
                    time.sleep(0.5)
                    self.action.escape()
                    purchased = True
                    break
                except NoSuchElementException:
                    time.sleep(1)
                    continue
            if cannot_afford:
                self.log.debug_append('cannot afford')
                self.log.debug_end()
                return BuyResult().buy_fail(FailReason.CANNOT_AFFORD)
            if no_space:
                Log('notify').info(f'[{self.user_id}]: no-space')
                self.log.debug_append('no-space')
                self.log.debug_end()
                return BuyResult().buy_fail(FailReason.NO_SPACE)
            if purchase_limit:
                Log('notify').info(f'[{self.user_id}]: exceeds-purchase-limit')
                self.log.debug_append('exceeds-purchase-limit')
                self.log.debug_end()
                return BuyResult().buy_fail(FailReason.EXCEEDS_PURCHASE_LIMIT)
            elif purchased:
                self.log.debug_append('purchased')
                self.log.debug_end()
                left_qty = left_qty - batch_buy_qty
                if left_qty <= 0:
                    return BuyResult().buy_success(buy_qty, 0)
            elif no_listing:
                self.action.click_by_offset(675, 165)
                auto_buy, listed_items = self.open_order_items(filter_input, item_name)
                time.sleep(0.5)
                auto_buy.click()
                time.sleep(0.5)
                self.log.debug_append('no-listings')
                self.log.debug_end()
            else:
                self.log.debug_append('not_purchased')
                self.log.debug_end()

    def sell_item(self, item_name, sell_qty, min_price=None, price_delta=0, sell_price=None):
        time.sleep(2)
        # 先取消已有订单
        pending_order = self.action.find_element(
            By.XPATH,
            f"//span[text()='{item_name}']/ancestor::div[2]/button[text()='Remove'][last()]",
            'LastPendingOrder',
            3
        )
        if pending_order is not None:
            pending_order.click()
            time.sleep(3)

        create_tab = self.driver.find_element(By.XPATH, '//button[text()="Create"]')
        create_tab.click()
        time.sleep(1)
        sell_item = self.action.find_element(By.XPATH, f"//span[text()='{item_name}']/parent::div/parent::div",
                                             item_name, 5)
        if sell_item is None:
            return
        time.sleep(2)

        add_button = sell_item.find_element(By.TAG_NAME, 'button')
        add_button.click()
        time.sleep(4)
        # 费率
        try:
            fee_label = self.action.find_element(By.XPATH, "//span[starts-with(text(), 'Fee')]", 'FeeLabel', 2)
            if fee_label is not None:
                label_text = fee_label.text
                match = re.search(r'Fee (-?\d+([.,]\d+)?)%', label_text)
                if match:
                    fee_percentage = match.group(1)
                    Account().save_fee_rate(self.user_id, fee_percentage)
        except:
            self.log.debug('get_fee_failed')
            pass
        floor_price = None
        for _ in range(10):
            try:
                floor_price_item = sell_item.find_element(
                    By.XPATH,
                    "//span[text()='Floor Price']/following-sibling::span"
                )
                if floor_price_item.text == '?':
                    time.sleep(1)
                    continue
                floor_price = self.get_number(floor_price_item.text)
            except NoSuchElementException:
                time.sleep(1)
                continue
        if floor_price is None:
            return
        price_input = self.action.find_element(By.XPATH, "//span[text()='Price per item']/following-sibling::input",
                                               'PriceInput', 5)
        if sell_price is None:
            sell_price = max(1, floor_price + price_delta)
        if min_price is not None:
            sell_price = max(sell_price, min_price)
        price_input.send_keys(str(sell_price))
        qty_input = self.action.find_element(By.XPATH,
                                             "//span[starts-with(text(), 'Quantity')]/following-sibling::input",
                                             'QtyInput', 5)

        time.sleep(1)
        qty_input.send_keys(Keys.CONTROL, 'a')
        qty_input.send_keys(Keys.BACKSPACE)
        qty_input.send_keys(str(sell_qty))
        time.sleep(1)
        sell_qty = qty_input.get_attribute('value')
        if self.last_trade_time is not None:
            duration = (datetime.datetime.now() - self.last_trade_time).seconds
            if duration < self.market_cooldown_seconds:
                time.sleep(self.market_cooldown_seconds - duration)
        create_button = self.action.find_element(By.CLASS_NAME, 'MarketplaceAddListing_button__Mzvuz', 'CreateButton',
                                                 5)
        create_button.click()
        self.log.debug(f"sell {item_name}, floor_price={floor_price}, sell_price={sell_price}, qty={sell_qty}")
        time.sleep(1)

    def save_p_orders(self, operation, task_item: TaskItem):
        if task_item.is_p:
            msg = f"[{self.user_id}] {operation} {task_item.reward} P, Need [{task_item.name}] x {task_item.qty}, Cost {task_item.get_cost()}"
            if operation == 'DELIVER':
                msg = f"[{self.user_id}] {operation} {task_item.reward} P, Give [{task_item.name}] x {task_item.qty}"
                self.game.snapshot(self.user_id, 'deliver_p', include_skills=False)
            Log('p_orders').info(msg)

    def get_floor_price(self, filter_input, item_name):
        floor_price = None
        _, order_items = self.open_order_items(filter_input, item_name)
        if len(order_items) != 0:
            floor_item = order_items[len(order_items) - 1]
            floor_price = self.get_number(floor_item.text.split(' @ ')[1])
        time.sleep(0.5)
        return floor_price

    def check_tasks(self):
        self.init_trade_config()
        self.log.debug('check task')
        orders_container = self.open_orders()
        time.sleep(4)
        order_items = self.action.find_sub_elements(orders_container, By.CLASS_NAME,
                                                    'Store_store-item-container__yxJbY',
                                                    'OrderItems')
        task_index = 0
        port_count = 12

        for order_item in order_items:
            task_index += 1
            try:
                button = order_item.find_element(By.CLASS_NAME, 'Store_btn-max__Vxg7c')
            except Exception:
                continue
            if button.text == 'Get VIP':
                port_count = 9
                continue
            if button.text == 'DELIVER':
                can_deliver = False
                if button.is_enabled():
                    can_deliver = True
                reward_elem = order_item.find_element(By.XPATH, '(.//span[@class="commons_coinCost__CbysW"])[2]')
                reward_icon = reward_elem.find_element(By.XPATH, "preceding-sibling::img")
                is_p = False
                name = order_item.find_element(By.CLASS_NAME, 'Store_card-title__InPpB').text
                if self.image_tool.find_target('pixel.png', base_elem=reward_icon) is not None:
                    is_p = True
                    reward = round(float(reward_elem.text.replace(',', '.')), 1)
                else:
                    reward = self.get_number(reward_elem.text)
                qty = int(order_item.find_element(By.XPATH, './/div[@class="Store_item-quantity__cFhDE"]/span').text)

                task_item = TaskItem(name, reward, qty, is_p, can_deliver, True)
                task_item.trade_config = self.trade_config
                if task_index in self.tasks:
                    task_item.is_new = False
                # 如果没有价格,查询并更新价格
                item_prices = self.get_item_prices()
                if task_item.price is None:
                    if task_item.name in item_prices:
                        task_item.price = item_prices.get(task_item.name)
                self.tasks[task_index] = task_item
        time.sleep(1)
        self.snapshot()
        return port_count

    def buy(self):
        self.log.debug('go shop')
        filter_input = self.open_market_buy()
        buy_items = {}
        for index in self.tasks:
            task_item: TaskItem = self.tasks.get(index)
            if not task_item.is_on_sale():
                if task_item.is_new:
                    self.log.debug(f"new_task {index} =>{task_item.to_json()}")
                    self.save_p_orders('FOUND', task_item)
                # 无法购买的任务跳过
                continue
            time.sleep(1)
            if task_item.price is None:
                # 查询价格
                floor_price = self.get_floor_price(filter_input, task_item.name)
                self.action.escape()
                task_item.price = floor_price
                self.save_item_price(task_item.name, floor_price)

            if task_item.is_new:
                self.log.debug(f"new_task {index} =>{task_item.to_json()}")
                self.save_p_orders('FOUND', task_item)

            my_coin = self.game.get_coin()
            if not task_item.can_deliver and task_item.can_afford(my_coin):
                buy_items[task_item.name] = buy_items.get(task_item.name, 0) + task_item.qty
                self.log.debug('buy:' + task_item.to_json())
                task_item.can_deliver = True
                task_item.purchased = True
            self.tasks[index] = task_item
            time.sleep(0.5)
            self.action.click_by_offset(680, 165)
            time.sleep(1)

        self.log.debug('batch_buy')
        for buy_name in buy_items:
            buy_qty = buy_items.get(buy_name)
            has_qty = self.game.get_backpack_qty(buy_name)
            buy_result = self.market_buy_item(filter_input, buy_name, buy_qty - has_qty)
            if buy_result.fail_reason == FailReason.NO_SPACE:
                self.log.info('no_space, stop buy and go deliver')
                break
            elif buy_result.fail_reason == FailReason.EXCEEDS_PURCHASE_LIMIT:
                self.log.info('exceeds-purchase-limit, stop buy and go deliver')
                break
        self.action.escape()

    def deliver(self):
        self.log.debug('go deliver')
        my_coin = self.game.get_coin()
        orders_container = self.open_orders()
        order_items = self.action.find_sub_elements(orders_container, By.CLASS_NAME,
                                                    'Store_store-item-container__yxJbY',
                                                    'OrderItems', 1)
        if order_items is None:
            self.action.escape()
            orders_container = self.open_orders()
            order_items = self.action.find_sub_elements(orders_container, By.CLASS_NAME,
                                                        'Store_store-item-container__yxJbY',
                                                        'OrderItems', 5)

        position = 0
        count = 0
        for order_item in order_items:
            position += 1
            try:
                button = order_item.find_element(By.CLASS_NAME, 'Store_btn-max__Vxg7c')
            except Exception:
                continue
            if button.text == 'Get VIP':
                continue
            if button.is_enabled():
                task_item: TaskItem = self.tasks.get(position)
                if task_item is None:
                    continue
                # 检查价格限制
                if self.can_trade and task_item.is_on_sale() and not task_item.purchased:  # 当前账号可交易且物品在售，并且不是购买的
                    if not task_item.can_afford(my_coin, True):  # 检查是否买得起
                        continue
                count += 1
                self.ac.reset_actions()
                self.ac.scroll_to_element(button).perform()
                time.sleep(1)
                button.click()
                self.save_deliver(position, task_item)
                del self.tasks[position]
                self.log.debug(f'deliver {position} =>' + task_item.to_json())
                self.save_p_orders('DELIVER', task_item)
                time.sleep(0.3)
        self.action.escape()
        time.sleep(1)
        return count
